/*
xp_can.c
offer can bus transfor data api realize,
designed by lunanting

-------------------
2022-02-16 14:38:53
create file
-------------------
*/

#include "xp_can.h"
#include "cli_cmd.h"

#define SPEED_can1 500  //unit:k
#define SPEED_can2 500  //unit: k

#define CAN_queue_size 16   //unit: can frame data * this size
#define CAN1_id  100 //can frame id
#define CAN2_id  101 //can frame id

static CAN_bus can1={0},can2={0};


static can_filter_item_t can_filter[]={
    {0, 0x00, 0x00},
};



static void xp_can_isrRxHanlder(can_frame_t *can_frame_rx,void *para);
static int32_t xp_can1_custmer(can_frameheader_t *rxheader,void *data,const uint32_t timeout);
static int32_t xp_can2_custmer(can_frameheader_t *rxheader,void *data,const uint32_t timeout);


/*
get bus obj from bus id
param:
    id: bus id
return: 
    bus obj pointer
*/
static CAN_bus *xp_can_bus_get(int id){
    if(id==1){
        return &can1;
    }
    else if(id==2){
        return &can2;
    }
    else{
        perr("can id input error, id= %d.",id);
        return NULL;
    }
}


/*
can bus init
param:
    id: can bus id, 1 or 2
    speed: can bus speed, unit= k, 100k=100000
    try: weather try send when send fail, 1=try,0=not try
    can_id: can frame id 
return:
    0=success
*/
int xp_can_bus_init(int id,int speed,int try,int can_id){
    CAN_bus *can;
    
    if(speed<0){
        perr("can %d speed set error, speed= %d.",id,speed);
        return -1;
    }
    can=xp_can_bus_get(id);
    if(can==NULL){
        return -2;
    }
    if(can->init==1)return 0;
    can->tx_frame.header.id=can_id;
    can->tx_frame.header.dlc=8;
    can->id=id;
    can->can_config.port=id;
    if(id==2){
        can->can_config.port=0;
    }
    can->can_config.config.baud_rate=speed*1000;
    if(try==0){
        can->can_config.config.auto_retry_transmit=0;
    }
    else{
        can->can_config.config.auto_retry_transmit=1;
    }

    can->rx_buf=(can_frame_t *)malloc(sizeof(can_frame_t)*CAN_queue_size);
    if(can->rx_buf==NULL){
        perr("can %d bus rx buffer malloc fail !",id);
        return -3;
    }
    
    if(aos_mutex_new(&can->bus_mux)!=0){
        perr("can %d bus mux create fail !",id);
        return -4;
    }
    if(aos_mutex_new(&can->send_mux)!=0){
        perr("can %d bus send mux create fail !",id);
        return -5;
    }

    if(aos_queue_new(&can->rx_queue,can->rx_buf,sizeof(can_frame_t)*CAN_queue_size,CAN_queue_size)!=0){
        perr("can %d bus queue create fail !",id);
        return -6;
    }

    hal_can_finalize(&can->can_config);
    if(hal_can_init(&can->can_config)!=0){
        perr("can %d bus hal init fail !",id);
        return -7;
    }

    can_mapping_t *can_mapping=can_mapping_node(can->can_config.port);
    if(can_mapping==NULL){
        perr("can %d mapping node not find !",id);
        return -8;
    }
    can_mapping->isr_rx_handler=xp_can_isrRxHanlder;
    if(id==1){
        can_mapping->customer_recv=xp_can1_custmer;
    }
    else if(id==2){
        can_mapping->customer_recv=xp_can2_custmer;
    }
    can_mapping->isr_rx_para=&can->rx_queue;

    if(hal_can_filter_init(&can->can_config,sizeof(can_filter)/sizeof(can_filter[0]),&can_filter)!=0){
        perr("can %d set can filter fail !",id);
        return -9;
    }

    if(hal_can_start(&can->can_config)!=0){
        perr("can %d start fail !",id);
        return -10;
    }

    can->init=1;
    println("can %d can bus init success !",id);
    return 0;
}




/*
isr rx hanlder api define
param:
    can_frame_rx: can接收的数据帧
    para: 保存数据帧的缓存，即can接收队列
*/
static void xp_can_isrRxHanlder(can_frame_t *can_frame_rx,void *para){
    if(can_frame_rx==NULL||para==NULL){
        return;
    }
    aos_queue_t *queue=(aos_queue_t *)para;
    aos_queue_send(queue,can_frame_rx,sizeof(can_frame_t)-(8-can_frame_rx->header.dlc));
}


/*
customer recive can data 
param:
    id: can bus id
    rxheader: can 的帧头
    data: can 接收的数据
    timeout: 最大等待时间 ms
return:
    0=success
*/
static int32_t xp_can_custmer(int id,can_frameheader_t *rxheader,void *data,const uint32_t timeout){
    int sta=0;
    can_frame_t can_frame;
    uint32_t size=0;
    CAN_bus *can;

    can=xp_can_bus_get(id);

    sta=aos_queue_recv(&can->rx_queue,timeout,&can_frame,&size);
    if(sta!=0){
        return sta;
    }
    memcpy(rxheader,&can_frame.header,sizeof(can_frameheader_t));
    if(can_frame.header.dlc > 0 && can_frame.header.rtr == 0 && data != NULL){
        memcpy(data,&can_frame.data,can_frame.header.dlc);
    }
    return 0;
}

/*
custmer recive can1 data
*/
static int32_t xp_can1_custmer(can_frameheader_t *rxheader,void *data,const uint32_t timeout){
    return xp_can_custmer(1,rxheader,data,timeout);
}

/*
custmer reciver can2 data
*/
static int32_t xp_can2_custmer(can_frameheader_t *rxheader,void *data,const uint32_t timeout){
    return xp_can_custmer(2,rxheader,data,timeout);
}



/*
device regist on can bus
param:
    dev: device obj
    dev_id: device id 0~127
    can_id: can bus id 1~2
return:
    0=success
*/
int xp_can_device_regist(CAN_dev *dev,int dev_id,int bus_id){
    CAN_bus *can;
    int sta=0;

    if(dev==NULL){
        return -3;
    }
    if(bus_id==1){
        sta=xp_can_bus_init(1,SPEED_can1,1,CAN1_id);
    }
    else if(bus_id==2){
        sta=xp_can_bus_init(2,SPEED_can2,1,CAN2_id);
    }
    if(sta!=0){
        return -1;
    }
    can=xp_can_bus_get(bus_id);
    if(can==NULL){
        return -2;
    }
    dev->id=dev_id;
    dev->bus=can;
    if(aos_sem_new(&dev->dev_sem,0)!=0){
        perr("device %d regist on can %d bus  create sem fail !",dev_id,bus_id);
        return -5;
    }
    if(aos_mutex_lock(&can->bus_mux,100)!=0){
        perr("device %d regist on can %d bus wait bus mux time out.",dev_id,bus_id);
        return -6;
    }
    slist_add_tail(&dev->next,&can->dev_list);
    aos_mutex_unlock(&can->bus_mux);
    println("device %d regist can %d bus success !",dev_id,bus_id);
    return 0;
}

/*
device unregist on can bus
param:
    dev: devive obj
return:
    0=success
*/
int xp_can_device_unregist(CAN_dev *dev){
    CAN_bus *can=dev->bus;
    if(dev==NULL)return -1;
    aos_sem_free(&dev->dev_sem);
    if(aos_mutex_lock(&can->bus_mux,100)!=0){
        perr("device %d regist on can %d bus wait mux time out.",dev->id,can->id);
        return -2;
    }
    slist_del(&dev->next,&can->dev_list);
    aos_mutex_unlock(&can->bus_mux);
}




/*
send data on can bus
param:
    can: can bus obj
    rtr: frame type,0= data, 1= remote
    data: data ,max 8 byte length
    len: send data lenght , bytes
return:
    0=success
*/
int xp_can_send(CAN_bus *can,int rtr,char *data,int len){
    int sta=0;
    if(can==NULL)return -1;
    if(aos_mutex_lock(&can->bus_mux,100)!=0){
        return -2;
    };
    if(rtr==0){
        can->tx_frame.header.rtr=0;
    }
    else{
        can->tx_frame.header.rtr=1;
    }
    if(len>8||len<0){
        len=8;
    }
    else if(len==0){
        return 0;
    }
    memcpy(can->tx_frame.data,data,len);
    can->tx_frame.header.dlc=len;
    sta=hal_can_send(&can->can_config,&can->tx_frame.header,&can->tx_frame.data,100);
    if(sta!=0){
        sta=-3;
    }
    aos_mutex_unlock(&can->bus_mux);
    return sta;
}


/*
recive data on can bus
param:
    can: can bus obj
    wait: wait time ms,0=wait forever
return:
    0=success
*/
int xp_can_recive(CAN_bus *can,int wait,can_frame_t *recv_can_frame){
    int sta=0;
    can_frame_t can_frame;
    CAN_dev *dev=NULL;
    slist_t *list;

    if(can==NULL)return -1;
    if(wait==0){
        wait=AOS_WAIT_FOREVER;
    }
    sta=hal_can_recv(&can->can_config,&can_frame.header,can_frame.data,wait);
    if(sta!=0){
        return -2;
    }
    memcpy(recv_can_frame,&can_frame,sizeof(can_frame_t));
    return 0;
}

// end of the file
